﻿using System;
using System.Collections.Specialized;
using Utils;

namespace StockModel
{
	internal class DailyKeyQuotationsCalculator : IDisposable
	{
		#region Attributes

		private readonly ReadWriteLockedObservableCollection<Quotation> _dailyQuotations;
		private readonly DateTime _day;

		#endregion

		#region Properties

		public DailyKeyQuotations DailyKeyQuotations { get; private set; }

		#endregion

		#region Public Methods

		public DailyKeyQuotationsCalculator(ReadWriteLockedObservableCollection<Quotation> dailyQuotations, DateTime day)
		{
			_day = day.Date;
			_dailyQuotations = dailyQuotations;
			_dailyQuotations.CollectionChanged += DailyQuotations_CollectionChanged; // Disposed

			this.DailyKeyQuotations = new DailyKeyQuotations();
		}

		#endregion

		#region Event Handlers

		private void DailyQuotations_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{

				if (e.NewItems.Count == 1)
				{
					Quotation quotation = (Quotation)e.NewItems[0];

					if (_day != quotation.Date.Date)
					{
						throw new InvalidOperationException(string.Format("Tried to load a quotation with date {0} into a daily info collection set to day {1}", quotation.Date.Date, _day));
					}

					if (this.DailyKeyQuotations.Opening == null && quotation.Volume > 0 && quotation.Type != QuoteType.OpeningBid)
					{
						this.DailyKeyQuotations.Opening = quotation;
						this.DailyKeyQuotations.LastOperation = quotation;
					}

					if ((this.DailyKeyQuotations.Max == null || this.DailyKeyQuotations.Max.Value < quotation.Value) && this.DailyKeyQuotations.Opening != null && quotation.Type != QuoteType.OpeningBid)
					{
						this.DailyKeyQuotations.Max = quotation;
					}

					if ((this.DailyKeyQuotations.Min == null || this.DailyKeyQuotations.Min.Value > quotation.Value) && this.DailyKeyQuotations.Opening != null && quotation.Type != QuoteType.OpeningBid)
					{
						this.DailyKeyQuotations.Min = quotation;
					}

					if (this.DailyKeyQuotations.Closing == null && quotation.Type == QuoteType.Closed)
					{
						this.DailyKeyQuotations.Closing = quotation;
					}

					if (this.DailyKeyQuotations.LastOperation != null && this.DailyKeyQuotations.LastOperation.Volume != quotation.Volume)
					{
						this.DailyKeyQuotations.LastOperation = quotation;
					}

					this.DailyKeyQuotations.LastQuotation = quotation;
				}
				else if (e.NewItems.Count > 1)
				{
					Quotation lastOperationTemp1 = this.DailyKeyQuotations.LastOperation == null ? null : (Quotation)this.DailyKeyQuotations.LastOperation.Clone();
					Quotation lastOperationTemp2 = null;
					Quotation maxTemp = this.DailyKeyQuotations.Max == null ? null : (Quotation)this.DailyKeyQuotations.Max.Clone();
					Quotation minTemp = this.DailyKeyQuotations.Min == null ? null : (Quotation)this.DailyKeyQuotations.Min.Clone();

					foreach (Quotation quotation in e.NewItems)
					{
						if (_day != quotation.Date.Date)
						{
							throw new InvalidOperationException(string.Format("Tried to load a quotation with date {0} into a daily info collection set to day {1}", quotation.Date.Date, _day));
						}

						if (this.DailyKeyQuotations.Opening == null && quotation.Volume > 0 && quotation.Type != QuoteType.OpeningBid)
						{
							this.DailyKeyQuotations.Opening = quotation;
							lastOperationTemp1 = quotation;
						}

						if ((maxTemp == null || maxTemp.Value < quotation.Value) && this.DailyKeyQuotations.Opening != null && quotation.Type != QuoteType.OpeningBid)
						{
							maxTemp = quotation;
						}

						if ((minTemp == null || minTemp.Value > quotation.Value) && this.DailyKeyQuotations.Opening != null && quotation.Type != QuoteType.OpeningBid)
						{
							minTemp = quotation;
						}

						if (this.DailyKeyQuotations.Closing == null && quotation.Type == QuoteType.Closed)
						{
							this.DailyKeyQuotations.Closing = quotation;
						}

						if (lastOperationTemp1 != null && lastOperationTemp1.Volume != quotation.Volume)
						{
							lastOperationTemp2 = lastOperationTemp1;
							lastOperationTemp1 = quotation;
						}
					}

					// This is a hack to allow the QuotationDailyStatisticsCalculator to calculate the last operation volume 
					if (lastOperationTemp2 != null)
					{
						this.DailyKeyQuotations.LastOperation = lastOperationTemp2;
					}
					this.DailyKeyQuotations.LastOperation = lastOperationTemp1;

					this.DailyKeyQuotations.Max = maxTemp;
					this.DailyKeyQuotations.Min = minTemp;
					this.DailyKeyQuotations.LastQuotation = (Quotation)e.NewItems[e.NewItems.Count - 1];
				}
			}
		}

		#endregion

		#region IDisposable

		protected bool Disposed { get; private set; }

		private void Dispose(bool disposing)
		{
			if (!this.Disposed)
			{
				this.InternalDispose(disposing);
			}

			this.Disposed = true;
		}

		protected virtual void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				_dailyQuotations.CollectionChanged -= DailyQuotations_CollectionChanged;
			}

			// Dispose here only unmanaged objects 
			// Don’t use managed objects here because maybe 
			// they have been finalized already
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		~DailyKeyQuotationsCalculator()
		{
			this.Dispose(false);
		}

		#endregion
	}
}
